﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using DotNetX.StringTokenization;

namespace DotNetX
{
    /// <summary>
    /// This tokenizes a passed string.
    /// </summary>
    public static class StringTokenizer
    {
        #region Properties

        /// <summary>
        /// The regex expression used to find tokens within the passed strings.
        /// </summary>
        public const string TokenPattern = @"\{((?<token>\w+)(:(?<modifier>.[^}]*))?)\}";

        /// <summary>
        /// The avaliable system parameters
        /// </summary>
        public static StringTokenizationParameterCollection SystemParameters
        {
            get
            {
                // do we have the system parameters?
                if (_systemParameters == null)
                {
                    // build the parameters
                    _systemParameters = BuildSystemParameters();
                    if (_systemParameters == null)
                        throw new NullReferenceException("'_systemParameters' is null.");
                }

                // return the parameters
                return _systemParameters;
            }
        }
        private static StringTokenizationParameterCollection _systemParameters = null;

        /// <summary>
        /// The current parameters to use when tokenizing
        /// </summary>
        private static StringTokenizationParameterCollection CurrentParameters
        {
            get
            {
                // return the parameters
                return _currentParameters;
            }
            set
            {
                // set the parameters
                _currentParameters = value;
            }
        }
        private static StringTokenizationParameterCollection _currentParameters = null;

        #endregion

        #region Public Methods

        /// <summary>
        /// This tokenizes the passed string.
        /// </summary>
        /// <param name="stringToTokenize">The string to tokenize.</param>
        /// <returns>The tokenized string.</returns>
        public static string Tokenize(string stringToTokenize)
        {
            if (stringToTokenize == null)
                throw new ArgumentNullException("'stringToTokenize' is null.");
            if (stringToTokenize.Length == 0)
                return string.Empty;

            // tokenize the string
            return StringTokenizer.Tokenize(stringToTokenize, null);
        }

        /// <summary>
        /// This tokenizes the passed string.
        /// </summary>
        /// <param name="stringToTokenize">The string to tokenize.</param>
        /// <param name="parameters">The custom parameters to tokenize the passed string with.</param>
        /// <returns>The tokenized string.</returns>
        public static string Tokenize(string stringToTokenize, StringTokenizationParameterCollection parameters)
        {
            if (stringToTokenize == null)
                throw new ArgumentNullException("'stringToTokenize' is null.");
            if (stringToTokenize.Length == 0)
                return string.Empty;

            // do we have any parameters?
            if (parameters == null ||
                parameters.Count == 0)
                return stringToTokenize;

            // create the regex
            Regex regex = new Regex(StringTokenizer.TokenPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);

            // set the parameters
            StringTokenizer.CurrentParameters = parameters;

            // tokenize the string
            stringToTokenize = regex.Replace(stringToTokenize, new MatchEvaluator(StringTokenizer.RegexTokenMatchEvaluator));

            // null the parameters
            StringTokenizer.CurrentParameters = null;

            // return the tokenized string
            return stringToTokenize;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// This creates a list of the avaliable system parameters
        /// </summary>
        /// <remarks>
        /// If a new system parameter is added, add the construction of it to this method
        /// </remarks>
        /// <returns>The system parameters</returns>
        private static StringTokenizationParameterCollection BuildSystemParameters()
        {
            // create the parameters
            StringTokenizationParameterCollection parameters = new StringTokenizationParameterCollection();

            // add the parameters
            parameters.Add(new DateTimeParameter());

            // return the system parameters
            return parameters;
        }

        /// <summary>
        /// This returns the value of the passed token to replace in the string.
        /// </summary>
        /// <param name="match">The token found.</param>
        /// <returns>The value of the token to replace.</returns>
        private static string RegexTokenMatchEvaluator(Match match)
        {
            if (match == null)
                throw new ArgumentNullException("'match' is null.");

            // make sure we have some parameters
            if (StringTokenizer.CurrentParameters == null ||
                StringTokenizer.CurrentParameters.Count == 0)
                return match.Value;

            // get the token
            string token = match.Groups["token"].Value;

            // get the modifier
            string modifier = match.Groups["modifier"].Value;

            // do we have the token in the custom parameters?
            if (CurrentParameters.ContainsKey(token))
            {
                // return the value
                return CurrentParameters[token].ToString(modifier);
            }

            // do we have the system parameter?
            if (SystemParameters.ContainsKey(token))
            {
                // return the value
                return SystemParameters[token].ToString(modifier);
            }
            else
            {
                // no token found, so return the token name
                return match.Value;
            }
        }

        #endregion
    }
}
